import {
  Component,
  OnInit,
  OnDestroy,
  ComponentFactoryResolver,
  Injector,
  ChangeDetectionStrategy,
  ChangeDetectorRef,
} from '@angular/core';
import { Subject, forkJoin, fromEvent } from 'rxjs';
import { takeUntil, filter, delay, tap } from 'rxjs/operators';
import { TranslateService } from '@ngx-translate/core';

import {
  WebAppSettingDataService,
  NewUrlStateNotificationService,
  UrlRouteManagerService,
  StoreHelperService,
  AnalyticsService,
  TRACKED_EVENT_LIST,
  DynamicPopupService,
  MessageQueueService,
  MESSAGE_TO,
} from 'app/shared/services';
import { UrlPath, UrlPathId, UrlQuery } from 'app/shared/models';
import { EndTime } from 'app/core/models';
import { ScatterChartDataService } from './scatter-chart-data.service';
import { ScatterChart } from './class/scatter-chart.class';
import { ScatterChartInteractionService } from './scatter-chart-interaction.service';
import {
  HELP_VIEWER_LIST,
  HelpViewerPopupContainerComponent,
} from 'app/core/components/help-viewer-popup/help-viewer-popup-container.component';

@Component({
  selector: 'pp-scatter-chart-container',
  templateUrl: './scatter-chart-container.component.html',
  styleUrls: ['./scatter-chart-container.component.css'],
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class ScatterChartContainerComponent implements OnInit, OnDestroy {
  private unsubscribe = new Subject<void>();
  private _hideSettingPopup = true;

  wrapperClassName = '';
  instanceKey = 'side-bar';
  addWindow = true;
  i18nText: { [key: string]: string };
  currentRange: { from: number; to: number } = {
    from: 0,
    to: 0,
  };
  selectedApplication: string;
  selectedAgent: string;
  typeCount: object;
  width = 460;
  height = 230;
  min: number;
  max: number;
  fromX: number;
  toX: number;
  fromY: number;
  toY: number;
  scatterChartMode: string;
  timezone: string;
  dateFormat: string[];
  showBlockMessagePopup = false;
  shouldHide = true;
  enableServerSideScan: boolean;
  sampleScatter: boolean;
  useScatterChartV2: boolean;

  constructor(
    private storeHelperService: StoreHelperService,
    private translateService: TranslateService,
    private webAppSettingDataService: WebAppSettingDataService,
    private newUrlStateNotificationService: NewUrlStateNotificationService,
    private urlRouteManagerService: UrlRouteManagerService,
    private scatterChartDataService: ScatterChartDataService,
    private scatterChartInteractionService: ScatterChartInteractionService,
    private analyticsService: AnalyticsService,
    private dynamicPopupService: DynamicPopupService,
    private componentFactoryResolver: ComponentFactoryResolver,
    private injector: Injector,
    private messageQueueService: MessageQueueService,
    private cd: ChangeDetectorRef
  ) {}

  ngOnInit() {
    this.webAppSettingDataService
      .getExperimentalConfiguration()
      .subscribe((configuration) => {
        const enableServerSideScan =
          this.webAppSettingDataService.getExperimentalOption('scatterScan');
        const sampleScatter =
          this.webAppSettingDataService.getExperimentalOption(
            'scatterSampling'
          );
        const useScatterChartV2 =
          this.webAppSettingDataService.getExperimentalOption(
            'useScatterChartV2'
          );

        this.enableServerSideScan =
          enableServerSideScan === null
            ? configuration.enableServerSideScanForScatter.value
            : enableServerSideScan;
        this.sampleScatter =
          sampleScatter === null
            ? configuration.sampleScatter.value
            : sampleScatter;
        this.useScatterChartV2 =
          useScatterChartV2 === null
            ? configuration.useScatterChartV2.value
            : useScatterChartV2;
      });

    this.setScatterY();

    forkJoin(
      this.translateService.get('COMMON.NO_DATA'),
      this.translateService.get('COMMON.FAILED_TO_FETCH_DATA'),
      this.translateService.get('COMMON.POPUP_BLOCK_MESSAGE')
    ).subscribe((i18n: string[]) => {
      this.i18nText = {
        NO_DATA: i18n[0],
        FAILED_TO_FETCH_DATA: i18n[1],
        POPUP_BLOCK_MESSAGE: i18n[2],
      };
    });

    this.newUrlStateNotificationService.onUrlStateChange$
      .pipe(
        takeUntil(this.unsubscribe),
        filter((urlService: NewUrlStateNotificationService) =>
          urlService.hasValue(UrlPathId.APPLICATION)
        )
      )
      .subscribe((urlService: NewUrlStateNotificationService) => {
        this.scatterChartDataService.stopLoad();
        this.scatterChartMode = urlService.isRealTimeMode()
          ? ScatterChart.MODE.REALTIME
          : ScatterChart.MODE.STATIC;
        this.selectedAgent = '';
        this.currentRange.from = this.fromX = urlService.getStartTimeToNumber();
        this.currentRange.to = this.toX = urlService.getEndTimeToNumber();
        this.cd.detectChanges();
      });

    this.scatterChartDataService.outScatterData$
      .pipe(takeUntil(this.unsubscribe))
      .subscribe((scatterData: IScatterData) => {
        if (!scatterData.complete) {
          this.scatterChartDataService.loadData(
            this.selectedApplication.split('^')[0],
            this.fromX,
            scatterData.resultFrom - 1,
            this.getGroupUnitX(),
            this.getGroupUnitY(),
            false
          );
        }

        this.scatterChartInteractionService.addChartData(
          this.instanceKey,
          scatterData
        );
        this.cd.detectChanges();
      });

    this.scatterChartDataService.outRealTimeScatterData$
      .pipe(takeUntil(this.unsubscribe))
      .subscribe((scatterData: IScatterData) => {
        this.scatterChartInteractionService.addChartData(
          this.instanceKey,
          scatterData
        );
        this.cd.detectChanges();
      });

    this.scatterChartDataService.onReset$
      .pipe(
        takeUntil(this.unsubscribe),
        tap(() => this.reset()),
        delay(1000)
      )
      .subscribe(() => {
        this.getScatterData();
        this.cd.detectChanges();
      });

    this.scatterChartDataService.outScatterErrorData$
      .pipe(takeUntil(this.unsubscribe))
      .subscribe((error: IServerError) => {
        this.scatterChartInteractionService.setError(error);
        this.cd.detectChanges();
      });
    this.scatterChartDataService.outRealTimeScatterErrorData$
      .pipe(takeUntil(this.unsubscribe))
      .subscribe((error: IServerError) => {});

    this.connectStore();
    this.addEventListener();
  }

  ngOnDestroy() {
    this.unsubscribe.next();
    this.unsubscribe.complete();
  }

  private reset(range?: { [key: string]: number }): void {
    this.toX = range ? range.toX : Date.now();
    this.fromX = range
      ? range.fromX
      : this.toX -
        this.webAppSettingDataService.getSystemDefaultPeriod().getMiliSeconds();

    this.scatterChartInteractionService.reset(
      this.instanceKey,
      this.selectedApplication,
      this.selectedAgent,
      this.fromX,
      this.toX,
      this.scatterChartMode
    );
  }

  private addEventListener(): void {
    const visibility$ = fromEvent(document, 'visibilitychange').pipe(
      takeUntil(this.unsubscribe),
      filter(() => this.scatterChartMode === ScatterChart.MODE.REALTIME)
    );

    // visible
    // visibility$.pipe(
    //     filter(() => !document.hidden),
    //     filter(() => !this.scatterChartDataService.isConnected()),
    //     tap(() => this.reset()),
    //     delay(1000)
    // ).subscribe(() => {
    //     this.getScatterData();
    //     this.cd.detectChanges();
    // });

    // hidden
    visibility$
      .pipe(
        filter(() => document.hidden),
        delay(10000),
        filter(() => document.hidden)
      )
      .subscribe(() => {
        this.scatterChartDataService.stopLoad();
        this.scatterChartInteractionService.stopRealtime();
      });
  }

  private setScatterY() {
    const { min, max } = this.webAppSettingDataService.getScatterY(
      this.instanceKey
    );

    this.fromY = min;
    this.toY = max;
  }

  private connectStore(): void {
    this.storeHelperService
      .getTimezone(this.unsubscribe)
      .subscribe((timezone: string) => {
        this.timezone = timezone;
        this.cd.detectChanges();
      });
    this.storeHelperService
      .getDateFormatArray(this.unsubscribe, 4, 5)
      .subscribe((format: string[]) => {
        this.dateFormat = format;
        this.cd.detectChanges();
      });
    this.storeHelperService
      .getAgentSelection(this.unsubscribe)
      .pipe(filter((agent: string) => this.selectedAgent !== agent))
      .subscribe((agent: string) => {
        this.selectedAgent = agent;
        this.scatterChartInteractionService.changeAgent(
          this.instanceKey,
          agent
        );
        this.cd.detectChanges();
      });
    this.messageQueueService
      .receiveMessage(this.unsubscribe, MESSAGE_TO.SERVER_MAP_TARGET_SELECT)
      .pipe(
        filter(({ isAuthorized }: ISelectedTarget) => isAuthorized),
        tap(({ isNode, isWAS, isMerged }: ISelectedTarget) => {
          this.shouldHide = !(isNode && isWAS && !isMerged);
          this.cd.detectChanges();
        })
      )
      .subscribe(({ node }: ISelectedTarget) => {
        if (!this.shouldHide) {
          this.selectedAgent = '';
          this.selectedApplication = node[0];
          this.reset(
            this.scatterChartMode === ScatterChart.MODE.REALTIME
              ? null
              : { fromX: this.fromX, toX: this.toX }
          );
          this.getScatterData();
        }

        this.cd.detectChanges();
      });
  }

  set hideSettingPopup(hide: boolean) {
    this._hideSettingPopup = hide;
    this.wrapperClassName = hide ? '' : 'l-popup-on';
  }

  get hideSettingPopup(): boolean {
    return this._hideSettingPopup;
  }

  getScatterData(): void {
    this.scatterChartDataService.loadData(
      this.selectedApplication.split('^')[0],
      this.fromX,
      this.toX,
      this.getGroupUnitX(),
      this.getGroupUnitY()
    );
    if (this.scatterChartMode === ScatterChart.MODE.REALTIME) {
      this.scatterChartDataService.loadRealTimeDataV2(this.toX);
    }
  }

  getGroupUnitX(): number {
    return Math.round((this.toX - this.fromX) / this.width);
  }

  getGroupUnitY(): number {
    return Math.round((this.toY - this.fromY) / this.height) || 1;
  }

  onApplySetting(params: any): void {
    this.analyticsService.trackEvent(
      TRACKED_EVENT_LIST.CHANGE_Y_RANGE_ON_SCATTER
    );
    this.fromY = params.min;
    this.toY = params.max;
    this.scatterChartInteractionService.changeYRange({
      instanceKey: this.instanceKey,
      from: params.min,
      to: params.max,
    });
    this.hideSettingPopup = true;
    this.webAppSettingDataService.setScatterY(this.instanceKey, {
      min: params.min,
      max: params.max,
    });
    this.reset({ fromX: this.fromX, toX: this.toX });
    this.getScatterData();
  }

  onCancelSetting(): void {
    this.hideSettingPopup = true;
  }

  onShowSetting(): void {
    this.analyticsService.trackEvent(TRACKED_EVENT_LIST.CLICK_SCATTER_SETTING);
    this.hideSettingPopup = false;
  }

  onDownload(): void {
    this.analyticsService.trackEvent(TRACKED_EVENT_LIST.DOWNLOAD_SCATTER);
    this.scatterChartInteractionService.downloadChart(this.instanceKey);
  }

  onOpenScatterPage(): void {
    this.analyticsService.trackEvent(
      TRACKED_EVENT_LIST.GO_TO_FULL_SCREEN_SCATTER
    );
    if (this.scatterChartMode === ScatterChart.MODE.STATIC) {
      this.urlRouteManagerService.openPage({
        path: [
          UrlPath.SCATTER_FULL_SCREEN_MODE,
          `${this.selectedApplication.replace('^', '@')}`,
          this.newUrlStateNotificationService
            .getPathValue(UrlPathId.PERIOD)
            .getValueWithTime(),
          this.newUrlStateNotificationService
            .getPathValue(UrlPathId.END_TIME)
            .getEndTime(),
          this.selectedAgent,
        ],
      });
    } else {
      this.urlRouteManagerService.openPage({
        path: [
          UrlPath.SCATTER_FULL_SCREEN_MODE,
          UrlPathId.REAL_TIME,
          `${this.selectedApplication.replace('^', '@')}`,
          this.selectedAgent,
        ],
      });
    }
  }

  onShowHelp($event: MouseEvent): void {
    this.analyticsService.trackEvent(
      TRACKED_EVENT_LIST.TOGGLE_HELP_VIEWER,
      HELP_VIEWER_LIST.SCATTER
    );
    const { left, top, width, height } = (
      $event.target as HTMLElement
    ).getBoundingClientRect();

    this.dynamicPopupService.openPopup(
      {
        data: HELP_VIEWER_LIST.SCATTER,
        coord: {
          coordX: left + width / 2,
          coordY: top + height / 2,
        },
        component: HelpViewerPopupContainerComponent,
      },
      {
        resolver: this.componentFactoryResolver,
        injector: this.injector,
      }
    );
  }

  onChangedSelectType(params: {
    instanceKey: string;
    name: string;
    checked: boolean;
  }): void {
    this.analyticsService.trackEvent(
      TRACKED_EVENT_LIST.CHANGE_SCATTER_CHART_STATE,
      `${params.name}: ${params.checked ? `on` : `off`}`
    );
    this.scatterChartInteractionService.changeViewType(params);
  }

  onChangeTransactionCount(params: object): void {
    this.typeCount = params;
  }

  onChangeRangeX(params: { from: number; to: number }): void {
    this.currentRange.from = params.from;
    this.currentRange.to = params.to;
    this.messageQueueService.sendMessage({
      to: MESSAGE_TO.REAL_TIME_SCATTER_CHART_X_RANGE,
      param: params,
    });
  }

  onSelectArea(params: any): void {
    this.analyticsService.trackEvent(TRACKED_EVENT_LIST.SELECT_AREA_ON_SCATTER);
    const { period, endTime } =
      this.newUrlStateNotificationService.isRealTimeMode()
        ? {
            period: this.webAppSettingDataService
              .getSystemDefaultPeriod()
              .getValueWithTime(),
            endTime: EndTime.newByNumber(this.currentRange.to).getEndTime(),
          }
        : {
            period: this.newUrlStateNotificationService
              .getPathValue(UrlPathId.PERIOD)
              .getValueWithTime(),
            endTime: this.newUrlStateNotificationService
              .getPathValue(UrlPathId.END_TIME)
              .getEndTime(),
          };

    const returnOpenWindow = this.urlRouteManagerService.openPage({
      path: [
        UrlPath.TRANSACTION_LIST,
        `${this.selectedApplication.replace('^', '@')}`,
        period,
        endTime,
      ],
      queryParams: {
        [UrlQuery.DRAG_INFO]: {
          x1: params.x.from,
          x2: params.x.to,
          y1: params.y.from,
          y2: params.y.to,
          agentId: this.selectedAgent,
          dotStatus: params.type,
        },
      },
    });

    if (returnOpenWindow === null || returnOpenWindow === undefined) {
      this.showBlockMessagePopup = true;
    }
  }

  onCloseBlockMessage(): void {
    this.showBlockMessagePopup = false;
  }
}
